#define _GNU_SOURCE         /* See feature_test_macros(7) */
#include <fcntl.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <sys/un.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/prctl.h>
#include <pthread.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
//#include <bits/fcntl-linux.h>

#include "somb.h"
#include "somb_log.h"

static struct somb_log_args
{
    int fd[2];
    char name[128];
    char log_file[256];
    st_somb_logfile_config stfileconfig;
    uint32_t log_file_current_head_index;
    uint32_t log_file_current_tail_index;
    uint32_t log_file_current_size;
}gstSombLogArgs={
    .log_file_current_size = 0,
    .log_file_current_tail_index = 0,
    .log_file_current_head_index = 0,
    .stfileconfig ={
        .log_level = 1,
    }
};

void elog_delete(void)
{
    int loop = 0, delta_file_num = 0;
    delta_file_num =gstSombLogArgs.log_file_current_head_index - gstSombLogArgs.log_file_current_tail_index ;
    if(delta_file_num >= gstSombLogArgs.stfileconfig.log_file_num)
    {
        char delfile[512];
        for(loop=0;loop<= (delta_file_num- gstSombLogArgs.stfileconfig.log_file_num);loop++)
        {
            memset(delfile,0,sizeof(delfile));
            sprintf(delfile,"%s/%s_%d.txt",gstSombLogArgs.stfileconfig.log_path,
                        gstSombLogArgs.name,gstSombLogArgs.log_file_current_tail_index);
            if(0 != remove(delfile))
            {
                printf("delete file(%s) failed,errno=%s\n",delfile,strerror(errno));
            }

            gstSombLogArgs.log_file_current_tail_index++;
        }
    }
}
void elog_compress(void)
{
    //TBD
    //compress oldfile , this may take some times, should not be done here!


    //update the file name and index
    gstSombLogArgs.log_file_current_head_index++;
    sprintf(gstSombLogArgs.log_file,"%s/%s_%d.txt",gstSombLogArgs.stfileconfig.log_path,
            gstSombLogArgs.name,gstSombLogArgs.log_file_current_head_index);

    /*delete old files*/
    elog_delete();
#if 0
    char oldfile[256];
    memset(oldfile,0,sizeof(oldfile));

    sprintf(oldfile,"%s",gstSombLogArgs.log_file);
    if(0 != rename(oldfile,gstSombLogArgs.log_file))
    {
        printf("somb log:(%s) pid(%d) rename logfile(%s) failed\n",gstSombLogArgs.name,getpid(),gstSombLogArgs.log_file);
    }
#endif

}

void *somb_log_thread(void *arg)
{
    
   int fd= open(gstSombLogArgs.log_file,O_CREAT|O_TRUNC|O_WRONLY|O_CLOEXEC,0666);
   if(0 > fd)
   {
        printf("somb log:(%s) pid(%d) open logfile(%s) failed\n",gstSombLogArgs.name,getpid(),gstSombLogArgs.log_file);
        return NULL;
   }

   //close(gstSombLogArgs.fd[1]);

    //uint8_t buffer[1024];
    //int32_t len;
    int ret;

#define ELOG_MAX_TRANSFER_BYTES   2028
    do
    {
        ret = splice(gstSombLogArgs.fd[0], NULL, fd, NULL, ELOG_MAX_TRANSFER_BYTES,0);
        if(0 >= ret) 
        {
            printf("somb log:(%s) pid(%d) write logfile(%s) failed,errno=%s \n",gstSombLogArgs.name,getpid(),gstSombLogArgs.name,strerror(errno));
        }
        else
        {
            gstSombLogArgs.log_file_current_size += ret;
            if(ret == ELOG_MAX_TRANSFER_BYTES)
            {
                continue;
            }

            if(gstSombLogArgs.log_file_current_size >= gstSombLogArgs.stfileconfig.log_file_size)
            {
                fdatasync(fd);
                gstSombLogArgs.log_file_current_size = 0;
                elog_compress();
               int newfd= open(gstSombLogArgs.log_file,O_CREAT|O_TRUNC|O_WRONLY|O_CLOEXEC,0666);
               if(0 > newfd)
               {
                    SOMB_LOG_ERR("somb log:(%s) pid(%d) open logfile(%s) failed\n",gstSombLogArgs.name,getpid(),gstSombLogArgs.log_file);
                    return NULL;
               }
                close(fd);
                fd = newfd;
            }
        }
#if 0
        memset(buffer,0,sizeof(buffer));
        len = read(gstSombLogArgs.fd[0],buffer,1024);
        if(len > 0)
        {
            len = fwrite(buffer,1,len,file);
            if(-1 == len)
            {
                printf("somb log:(%s) pid(%d) write logfile failed\n",gstSombLogArgs.name,getpid());
            }
            else
            {
                fflush(file);
            }
        }
        else
        {
            printf("somb log:(%s) pid(%d) read pipe failed\n",gstSombLogArgs.name,getpid());
        }
#endif
    }while(1);
   
}

int logfile_entry_filter(const struct dirent *entry)
{
    if(NULL != strstr(entry->d_name,gstSombLogArgs.name))
    {
        return 1;
    }

    return 0;
}

int32_t somb_log_init(st_somb_logfile_config *stlogfileconf)
{
    char strProcessPath[128] = {0};
    if(0 != prctl(PR_GET_NAME,strProcessPath,0,0,0))
    {
        sprintf(gstSombLogArgs.name,"pid%d",getpid());
    }
    sprintf(gstSombLogArgs.name,"%s",strProcessPath);

    memcpy(&(gstSombLogArgs.stfileconfig),stlogfileconf,sizeof(st_somb_logfile_config));

    /*get current log file num*/

    struct dirent **entry_list;
    int count;
    int i;

    struct stat st = {0};
    if (stat(stlogfileconf->log_path, &st) == 0)
    {
        //log path exit,do nothing
        if(!S_ISDIR(st.st_mode))
        {
            unlink(stlogfileconf->log_path);
            //log path not exit
            mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO; // recursive permissions
            if (mkdir(stlogfileconf->log_path, mode) == 0) {
                printf("somb log:delete and created Directory successfully,%s\n",stlogfileconf->log_path);
            } else {
                printf("somb log:Delete and creating directory Error %s\n",stlogfileconf->log_path);
                return -1;
            }
        }
    }
    else
    {
        //log path not exit
        mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO; // recursive permissions
        if (mkdir(stlogfileconf->log_path, mode) == 0) {
            printf("somb log:Directory created successfully,%s\n",stlogfileconf->log_path);
        } else {
            printf("somb log:Error creating directory %s\n",stlogfileconf->log_path);
            return -1;
        }
    }

    count = scandir(stlogfileconf->log_path, &entry_list, logfile_entry_filter, alphasort);
    if (count < 0) {
        printf("somb log:scandir log path[%s] failed,errno=%s\n",stlogfileconf->log_path,strerror(errno));
        return EXIT_FAILURE;
    }

    char tmp_match_fmt[256];
    sprintf(tmp_match_fmt,"%s_%%d.txt",gstSombLogArgs.name);
    //printf("matching str=%s\n",tmp_match_fmt);
    int max_index = -1;
    uint min_index = 99999999;
    for (i = 0; i < count; i++) {
        int tmp_index =0 ;
        struct dirent *entry;
        entry = entry_list[i];
        if(sscanf(entry->d_name,tmp_match_fmt,&tmp_index) > 0)
        {
            //printf("%s index=%d\n", entry->d_name,tmp_index);
            max_index = (tmp_index >max_index)?tmp_index:max_index;
            min_index = (tmp_index < min_index)?tmp_index:min_index;
        }
        free(entry);
    }
    free(entry_list);

    if(max_index == -1)
    {
        gstSombLogArgs.log_file_current_head_index = 0;
        gstSombLogArgs.log_file_current_tail_index = 0;
    }
    else
    {
        gstSombLogArgs.log_file_current_tail_index = min_index;
        max_index++;
        gstSombLogArgs.log_file_current_head_index = max_index;
    }

    elog_delete();

    char log_path_file[127];
    bzero(log_path_file,127);
    //sprintf(gstSombLogArgs.log_file,"%s/%s_%d",log_path,strProcessPath,getpid());
    sprintf(gstSombLogArgs.log_file,"%s/%s_%d.txt",stlogfileconf->log_path,strProcessPath,gstSombLogArgs.log_file_current_head_index);

    gstSombLogArgs.fd[0] = -1;
    gstSombLogArgs.fd[1] = -1;

    if(0 > pipe(gstSombLogArgs.fd))
    {
        printf("somb log:(%s) pid(%d) create pipe failed\n",strProcessPath,getpid());
        return -1;
    }

    int flags = fcntl(gstSombLogArgs.fd[1], F_GETFL);
    if(-1 != flags)
    {
        flags |= O_NONBLOCK;
        if(-1 == fcntl(gstSombLogArgs.fd[1], F_SETFL, flags))
        {
            perror("Failed to set file status flags");
        }
    }
    else {
        perror("Failed to get file status flags");
    }


    pthread_t tid;
    if(0 > pthread_create(&tid, NULL,somb_log_thread , NULL))
    {
        printf("somb log:(%s) pid(%d) create log thread failed\n",strProcessPath,getpid());
        return -2;
    }
   //close(gstSombLogArgs.fd[0]); 

    return 0;
}

void somb_log_record(int32_t level,char *fmt,...)
{

    if((-1 != gstSombLogArgs.fd[1])&&(level >= gstSombLogArgs.stfileconfig.log_level))
    {
        char _tmp_char_level_=' ';
        switch(level)
        {
            case SOMB_LOG_LEVEL_DEBUG: 
                _tmp_char_level_ = 'D'; 
                break;
            case SOMB_LOG_LEVEL_INFO: 
                _tmp_char_level_ = 'I'; 
                break;
            case SOMB_LOG_LEVEL_WARN: 
                _tmp_char_level_ = 'W'; 
                break;
            case SOMB_LOG_LEVEL_ERROR: 
                _tmp_char_level_ = 'E'; 
                break;
            default:
                break;
        }
        struct timeval __timeval; \
        gettimeofday(&__timeval,NULL); \
        struct tm __tm_now; \
        memset(&__tm_now,0,sizeof(struct tm));\
        localtime_r(&(__timeval.tv_sec),&__tm_now);\
        char __time_str[32];\
        memset(__time_str,0,sizeof(__time_str));\
        ssize_t len = sprintf(__time_str,"%c %04d-%02d-%02d %02d:%02d:%02d.%06ld ",_tmp_char_level_,\
                    __tm_now.tm_year+1900,__tm_now.tm_mon+1,__tm_now.tm_mday,\
                    __tm_now.tm_hour,  __tm_now.tm_min, __tm_now.tm_sec,__timeval.tv_usec);\

        write(gstSombLogArgs.fd[1],__time_str,len);
        va_list ap;
        va_start(ap, fmt);
        vdprintf(gstSombLogArgs.fd[1],fmt,ap);
        va_end(ap);
    }
}



